package netty.heartbeat;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.util.concurrent.EventExecutorGroup;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GenericFutureListener;
import io.netty.util.concurrent.ScheduledFuture;

import java.util.Random;
import java.util.concurrent.TimeUnit;

/**
 * @author: 程序员七哥
 * @date: 2021-12-13
 * @description: 随机发送心跳处理器
 */
public class HeartBeatHandler extends ChannelInboundHandlerAdapter {

    private Bootstrap bootstrap;

    private ScheduledFuture<?> schedule;
    private GenericFutureListener listener;

    public HeartBeatHandler(Bootstrap bootstrap) {
        this.bootstrap = bootstrap;
    }

    /**连接成功后触发*/
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        // 随机发送心跳
        randomSend(ctx.channel());
    }

    private void randomSend(Channel channel) {
        // 生成一个[1，8）范围内的随机数，作为发送心跳的时间间隔
        int heartBeatInterval = new Random().nextInt(7) + 1 ;
        System.out.println(channel.localAddress() + ": " + heartBeatInterval + "秒后发送下一次心跳");
        // 获取 channel 对应的 eventLoop，添加一个异步的定时任务
        schedule = channel.eventLoop().schedule(() -> {
            if (channel.isActive()) {
                System.out.println(channel.localAddress() + ": " + "向服务器发送心跳");
                channel.writeAndFlush("~PING~");
            } else {
                System.out.println(channel.localAddress() + ": " + "与服务器的连接已经断开");
                // 若Channel被关闭，则会触发该future的监听器的回调执行
                final ChannelFuture closeFuture = channel.closeFuture();
                // 如果连接被服务端关闭则移除定时器
                closeFuture.addListener((future) -> {
                    schedule.removeListener(listener);
                });
            }
        }, heartBeatInterval, TimeUnit.SECONDS);

        listener = (p) -> {
            randomSend(channel);
        };
        schedule.addListener(listener);
    }

    /**连接断开会触发此事件*/
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        // 每隔 1 秒进行重连
        ctx.channel().eventLoop().schedule(()->{
            System.out.println("客户端开始重连。。。");
            try {
                bootstrap.connect("localhost", 8888).sync();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },1,TimeUnit.SECONDS);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        // 关闭连接
        ctx.close();
    }
}
